home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Toolbox / Visual Basic Toolbox (P.I.E.)(1996).ISO / listbox / listtab / dulist.bas next >
Encoding:
BASIC Source File  |  1996-01-05  |  14.2 KB  |  478 lines

  1. Attribute VB_Name = "DULIST"
  2. 'This file has been updated from the original posting on CSERVE in 1993.  It has been
  3. 'modified to support the WIN32 SDK API calls and works with 32bit VB4.
  4. 'Robert Wallace
  5. '74604,501'
  6.  
  7. Option Explicit
  8.  
  9. Const WM_USER = &H400
  10. Const LB_SETTABSTOPS = &H192
  11. Const EM_SETTABSTOPS = &HCB
  12. Const CB_SELECTSTRING = &H14D
  13. Const LB_SELECTSTRING = &H18C
  14. Const LB_SETHORIZONTALEXTENT = &H194
  15. Type Size
  16.      X As Long
  17.      Y As Long
  18. End Type
  19. Public SizeStruct As Size
  20. Const nSEARCH_FROM_TOP = -1
  21.  
  22. Declare Function dulist_nlSetTabstops Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long
  23. Declare Function dulist_nlSelectString Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long
  24. Declare Function dulist_nlGetTextExtent Lib "gdi32" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As Size) As Long
  25. Declare Function dulist_nlGetDialogBaseUnits Lib "user32" Alias "GetDialogBaseUnits" () As Long
  26. Declare Function dulist_nlSetHorizScrollBar Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long
  27. Sub dulist_AddHorizScrollBar(ctlListControl As Control, fVirtualWidthRatio As Single)
  28.  
  29. Dim nlRC As Long
  30. Dim fMultiplier As Single
  31.  
  32.  
  33. If fVirtualWidthRatio <= 1 Then
  34.    fMultiplier = 2  'default 2x wider
  35. Else
  36.    fMultiplier = fVirtualWidthRatio
  37. End If
  38.  
  39. nlRC = dulist_nlSetHorizScrollBar(ctlListControl.hwnd, LB_SETHORIZONTALEXTENT, (ctlListControl.Width * fMultiplier) \ Screen.TwipsPerPixelX, 0)
  40.  
  41. End Sub
  42.  
  43. Function dulist_sGetColumn(sColData As String, nColID As Integer) As String
  44.  
  45. Dim sTAB As String
  46. Dim sColString As String
  47. Dim nNbrListboxCols As Integer
  48. Dim nInStart As Integer, nTabPos As Integer
  49.  
  50.  
  51. dulist_sGetColumn = ""
  52.  
  53. If Len(sColData) = 0 Or nColID <= 0 Then
  54.    Exit Function
  55. End If
  56.  
  57. sTAB = Chr$(9)
  58. nNbrListboxCols = 1
  59.  
  60. nInStart = 1
  61. Do
  62.    nTabPos = InStr(nInStart, sColData, sTAB)
  63.  
  64.    If nTabPos > 0 Then
  65.       sColString = Mid$(sColData, nInStart, nTabPos - nInStart)
  66.    Else
  67.       sColString = Mid$(sColData, nInStart, Len(sColData) - nInStart + 1)
  68.    End If
  69.  
  70.    If nNbrListboxCols = nColID Then
  71.       dulist_sGetColumn = RTrim$(sColString)
  72.       Exit Do
  73.    End If
  74.  
  75.    If nTabPos > 0 Then
  76.       nNbrListboxCols = nNbrListboxCols + 1
  77.  
  78.       If nTabPos < Len(sColData) Then
  79.          nInStart = nTabPos + 1
  80.       Else
  81.          Exit Do
  82.       End If
  83.    Else
  84.       Exit Do
  85.    End If
  86. Loop
  87.  
  88. End Function
  89.  
  90. Function dulist_tfSelectListItem(ctlListControl As Control, sSelectString As String) As Integer
  91.  
  92. Dim nMsgID As Integer
  93. Dim nlRC As Long
  94.  
  95.  
  96. '===================
  97. SelectListItem_Main:
  98. '===================
  99. dulist_tfSelectListItem = True
  100.  
  101. GoSub SelectListItem_VerifyControls
  102. GoSub SelectListItem_UpdateControls
  103.  
  104. Exit Function
  105.  
  106.  
  107. '=============================
  108. SelectListItem_VerifyControls:
  109. '=============================
  110. If TypeOf ctlListControl Is ListBox Then
  111.    nMsgID = LB_SELECTSTRING
  112. Else
  113.    If TypeOf ctlListControl Is ComboBox Then
  114.       nMsgID = CB_SELECTSTRING
  115.    Else
  116.       dulist_tfSelectListItem = False
  117.       Exit Function
  118.    End If
  119. End If
  120.  
  121. If Len(sSelectString) = 0 Then
  122.    dulist_tfSelectListItem = False
  123.    Exit Function
  124. End If
  125.  
  126. Return
  127.  
  128. '=============================
  129. SelectListItem_UpdateControls:
  130. '=============================
  131. nlRC = dulist_nlSelectString(ctlListControl.hwnd, nMsgID, nSEARCH_FROM_TOP, sSelectString)
  132.  
  133. Return
  134.  
  135. End Function
  136.  
  137. Function dulist_tfSetListCols(ctlListControl As Control, ctlTextControl As Control, tfUseHeadingWidthsOnly As Integer, tfSetDefaultTabstops As Integer) As Integer
  138.  
  139. 'This function automatically calculates and sets appropriate
  140. 'tabstops for a multi-column listbox, based on the actual data
  141. 'in the listbox.  You do not have to tell the function how many
  142. 'columns you want, nor figure out how wide each column should be;
  143. 'the actual data placed into the listbox determines that.
  144.  
  145. 'In addition to the listbox, the function also sets identical
  146. 'tabstops in an accompanying, multi-line textbox.  This textbox
  147. 'provides the data for the column headings.
  148.  
  149. 'tfUseHeadingWidthsOnly:
  150. '  True -  Tabstops are calculated based only on the
  151. '          widths of the column headings. This option
  152. '          is must faster, but you're gambling that the
  153. '          actual data will always be narrower than the
  154. '          headings.
  155. '
  156. '  False - Tabstops are calculated based on the widest
  157. '          entry in each column; both the headings and
  158. '          the data are examined.  This option is slower
  159. '          because each entry in the listbox must be
  160. '          parsed, but it eliminates the guesswork.
  161.  
  162. 'tfSetDefaultTabstops:
  163. '  True -  Tabstops are reset to Windows' default intervals
  164. '          of 8 dialog units.
  165. '
  166. '  False - Tabstops are calculated based on the actual
  167. '          data in the listbox/textbox.
  168. '
  169. '
  170. 'The function itself returns FALSE if any of the control
  171. 'verification tests fail; otherwise it returns TRUE.
  172.  
  173.  
  174. Dim sTAB As String
  175. Dim sColHeadings As String, sColData As String, sColString As String
  176. Dim sParentFontName As String, fParentFontSize As Single
  177. Dim tfParentFontBold As Integer, tfParentFontItalic As Integer
  178. Dim nColCount As Integer, nDataWidth As Integer, nSpaceBetweenCols As Integer
  179. Dim nMaxListboxCols As Integer, nNbrListboxCols As Integer, nNbrTabstops As Integer
  180. Dim nInStart As Integer, nTabPos As Integer
  181. Dim nListSub As Integer, nTabSub As Integer
  182. Dim nlRC As Long
  183. Dim nListFontAvgWidth As Integer, nSystemFontAvgWidth As Integer
  184. Dim fListFontPixelsPerDlgUnit As Single, fFontRatio As Single
  185.  
  186. Dim nColWidth() As Integer  'measured column widths
  187. Dim nTabstop() As Long   'calculated WinAPI tabstops
  188. Dim RetVal As Long
  189. '================
  190. SetListCols_Main:
  191. '================
  192. dulist_tfSetListCols = True
  193.  
  194. GoSub SetListCols_VerifyControls
  195. GoSub SetListCols_Initialize
  196.  
  197. If tfSetDefaultTabstops Then
  198.    nNbrTabstops = 0
  199.    GoSub SetListCols_UpdateControls
  200. Else
  201.    'Since VB provides an hDC property for forms, but
  202.    'not for controls, we must temporarily set the parent
  203.    'form's font characteristics equal to the listbox's
  204.    'font characteristics.  Doing this ensures that all
  205.    'text measurements made using the form's DC will be
  206.    'accurate for the listbox.
  207.  
  208.    sParentFontName = ctlListControl.Parent.FontName
  209.    fParentFontSize = ctlListControl.Parent.FontSize
  210.    tfParentFontBold = ctlListControl.Parent.FontBold
  211.    tfParentFontItalic = ctlListControl.Parent.FontItalic
  212.    ctlListControl.Parent.FontName = ctlListControl.FontName
  213.    ctlListControl.Parent.FontSize = ctlListControl.FontSize
  214.    ctlListControl.Parent.FontBold = ctlListControl.FontBold
  215.    ctlListControl.Parent.FontItalic = ctlListControl.FontItalic
  216.  
  217.    'Identify and measure the width of the column headings
  218.    'present in the textbox.
  219.  
  220.    GoSub SetListCols_MeasureColHeadingWidths
  221.  
  222.    'Measure the width of the column data values present
  223.    'in the listbox.
  224.  
  225.    If Not tfUseHeadingWidthsOnly Then
  226.       GoSub SetListCols_MeasureColDataWidths
  227.    End If
  228.  
  229.    'Calculate and set the necessary tabstop values, based
  230.    'on the maximum width of each column.
  231.  
  232.    GoSub SetListCols_UpdateControls
  233.  
  234.    'Reset the parent form's font characteristics to their
  235.    'original values.
  236.  
  237.    ctlListControl.Parent.FontName = sParentFontName
  238.    ctlListControl.Parent.FontSize = fParentFontSize
  239.    ctlListControl.Parent.FontBold = tfParentFontBold
  240.    ctlListControl.Parent.FontItalic = tfParentFontItalic
  241. End If
  242.  
  243. Exit Function
  244.  
  245.  
  246. '==========================
  247. SetListCols_VerifyControls:
  248. '==========================
  249. 'Make sure both controls are of the proper type,
  250. 'and that the necessary property values are set.
  251.  
  252. If TypeOf ctlListControl Is ListBox Then
  253. Else
  254.    dulist_tfSetListCols = False
  255.    Exit Function
  256. End If
  257.  
  258. If TypeOf ctlTextControl Is TextBox Then
  259. Else
  260.    dulist_tfSetListCols = False
  261.    Exit Function
  262. End If
  263.  
  264. If ctlListControl.Columns <> 0 Then
  265.    dulist_tfSetListCols = False
  266.    Exit Function
  267. End If
  268.  
  269. If ctlTextControl.MultiLine = False Then
  270.    dulist_tfSetListCols = False
  271.    Exit Function
  272. End If
  273.  
  274. If ctlTextControl.BorderStyle <> 0 Then
  275.    dulist_tfSetListCols = False
  276.    Exit Function
  277. End If
  278.  
  279. If Len(ctlTextControl.Text) = 0 Then
  280.    dulist_tfSetListCols = False
  281.    Exit Function
  282. End If
  283.  
  284. Return
  285.            
  286. '======================
  287. SetListCols_Initialize:
  288. '======================
  289. 'A little extra space between columns helps
  290. 'to mitigate the inevitable rounding errors
  291. 'that will occur in the tabstop calculations.
  292.  
  293. nSpaceBetweenCols = 2
  294.  
  295. nMaxListboxCols = 10
  296. ReDim nColWidth(nMaxListboxCols)
  297.  
  298. sTAB = Chr$(9)
  299.  
  300. Return
  301.  
  302. '===================================
  303. SetListCols_MeasureColHeadingWidths:
  304. '===================================
  305. 'Search for TAB characters in the column heading
  306. 'text.  For each column found, measure the width
  307. 'of the heading text.
  308.  
  309. sColHeadings = ctlTextControl.Text
  310. nNbrListboxCols = 1
  311.  
  312. nInStart = 1
  313. Do
  314.    nTabPos = InStr(nInStart, sColHeadings, sTAB)
  315.  
  316.    If nTabPos > 0 Then
  317.       sColString = Mid$(sColHeadings, nInStart, nTabPos - nInStart)
  318.    Else
  319.       sColString = Mid$(sColHeadings, nInStart, Len(sColHeadings) - nInStart + 1)
  320.    End If
  321.  
  322.    'Measure the length of the string, in pixels;
  323.    'this value is the current "column width".
  324.    
  325.    sColString = sColString + Space$(nSpaceBetweenCols)
  326.    RetVal = dulist_nlGetTextExtent(ctlListControl.Parent.hdc, sColString, Len(sColString), SizeStruct)
  327.    nColWidth(nNbrListboxCols) = SizeStruct.X Mod 65536
  328.    If nTabPos > 0 Then
  329.       nNbrListboxCols = nNbrListboxCols + 1
  330.  
  331.       'Allocate space for more columns, if necessary
  332.  
  333.       If nNbrListboxCols > nMaxListboxCols Then
  334.          nMaxListboxCols = nNbrListboxCols
  335.          ReDim Preserve nColWidth(nMaxListboxCols)
  336.       End If
  337.  
  338.       If nTabPos < Len(sColHeadings) Then
  339.          nInStart = nTabPos + 1
  340.       Else
  341.          Exit Do
  342.       End If
  343.    Else
  344.       Exit Do
  345.    End If
  346. Loop
  347.  
  348. nNbrTabstops = nNbrListboxCols - 1
  349.  
  350. Return
  351.  
  352. '================================
  353. SetListCols_MeasureColDataWidths:
  354. '================================
  355. 'Search for TAB characters in the listbox data.
  356. 'For each column found, measure the width of
  357. 'the data.
  358.  
  359. For nListSub = 0 To ctlListControl.ListCount - 1
  360.    If Len(ctlListControl.List(nListSub)) > 0 Then
  361.       sColData = ctlListControl.List(nListSub)
  362.       nColCount = 1
  363.  
  364.       nInStart = 1
  365.       Do
  366.          nTabPos = InStr(nInStart, sColData, sTAB)
  367.  
  368.          If nTabPos > 0 Then
  369.             sColString = Mid$(sColData, nInStart, nTabPos - nInStart)
  370.          Else
  371.             sColString = Mid$(sColData, nInStart, Len(sColData) - nInStart + 1)
  372.          End If
  373.  
  374.          'Measure the length of the string, in pixels
  375.    
  376.          sColString = sColString + Space$(nSpaceBetweenCols)
  377.          RetVal = dulist_nlGetTextExtent(ctlListControl.Parent.hdc, sColString, Len(sColString), SizeStruct)
  378.          nDataWidth = SizeStruct.X Mod 65536
  379.          'Ignore data columns for which there is no heading.
  380.  
  381.          If nColCount <= nNbrListboxCols Then
  382.             'If any data value is wider than the current column width,
  383.             'it becomes the new column width.
  384.  
  385.             If nDataWidth > nColWidth(nColCount) Then
  386.                nColWidth(nColCount) = nDataWidth
  387.             End If
  388.          End If
  389.  
  390.          If nTabPos > 0 Then
  391.             nColCount = nColCount + 1
  392.  
  393.             If nTabPos < Len(sColData) Then
  394.                nInStart = nTabPos + 1
  395.             Else
  396.                Exit Do
  397.             End If
  398.          Else
  399.             Exit Do
  400.          End If
  401.       Loop
  402.    End If
  403. Next
  404.  
  405. Return
  406.  
  407. '==========================
  408. SetListCols_UpdateControls:
  409. '==========================
  410. 'Set the textbox font characteristics equal
  411. 'to the listbox font characteristics.
  412.  
  413. ctlTextControl.Enabled = False
  414. ctlTextControl.FontName = ctlListControl.FontName
  415. ctlTextControl.FontSize = ctlListControl.FontSize
  416. ctlTextControl.FontBold = ctlListControl.FontBold
  417. ctlTextControl.FontItalic = ctlListControl.FontItalic
  418. ctlTextControl.Move ctlListControl.Left, ctlListControl.Top - ctlTextControl.Height, ctlListControl.Width, ctlTextControl.Height
  419.  
  420. ReDim nTabstop(nNbrTabstops)
  421.  
  422. 'Calculate tabstop values for each column, in "dialog units"
  423.  
  424. If nNbrTabstops > 0 Then
  425.    'Get the average character widths, in pixels, of the
  426.    'listbox font and the system font.
  427.  
  428.    RetVal = dulist_nlGetTextExtent(ctlListControl.Parent.hdc, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, SizeStruct)
  429.    nListFontAvgWidth = (SizeStruct.X Mod 65536) / 52
  430.    nSystemFontAvgWidth = dulist_nlGetDialogBaseUnits() Mod 65536
  431.  
  432.    'A "dialog unit" is defined as 1/4 of the average
  433.    'character width of the system font, in pixels.
  434.    'We've already measured the width of each column,
  435.    'in pixels, but it's not accurate enough to simply
  436.    'divide one value into the other.
  437.  
  438.    'Note that errors in precision will start to creep in
  439.    'at this point, due to integer rounding and intermediate
  440.    'calculation results.  Experience shows that a little
  441.    'extra white space between the data columns helps to
  442.    'compensate (see "nSpaceBetweenCols").
  443.  
  444.    'Since a dialog unit is based on the system font,
  445.    'not the font we're actually using in the listbox,
  446.    'we must factor in the difference between the two
  447.    'average character widths.  Thus, a more accurate
  448.    'divisor is calculated as follows.
  449.  
  450.    fFontRatio = nListFontAvgWidth / nSystemFontAvgWidth
  451.    fListFontPixelsPerDlgUnit = (nSystemFontAvgWidth * fFontRatio) / 4
  452.  
  453.    'Set a tabstop at the dialog unit closest to the
  454.    'right-hand boundary (width) of each column.
  455.  
  456.    nTabstop(0) = nColWidth(1) / fListFontPixelsPerDlgUnit
  457.    For nTabSub = 2 To nNbrTabstops
  458.       nTabstop(nTabSub - 1) = nTabstop(nTabSub - 2) + nColWidth(nTabSub) / fListFontPixelsPerDlgUnit
  459.    Next
  460. Else
  461.    nTabstop(0) = 0
  462. End If
  463.  
  464. 'Activate the tabstops.
  465.  
  466. nlRC = dulist_nlSetTabstops(ctlTextControl.hwnd, EM_SETTABSTOPS, nNbrTabstops, nTabstop(0))
  467. nlRC = dulist_nlSetTabstops(ctlListControl.hwnd, LB_SETTABSTOPS, nNbrTabstops, nTabstop(0))
  468.  
  469. 'Redraw the controls.
  470.  
  471. ctlTextControl.Refresh
  472. ctlListControl.Refresh
  473.  
  474. Return
  475.  
  476. End Function
  477.  
  478.